package store.lunangangster.bpmn.converter;

import java.util.List;
import javax.xml.stream.XMLStreamReader;
import store.lunangangster.bpmn.converter.parser.AbstractBpmnElementParser;
import store.lunangangster.bpmn.converter.parser.DocumentationParser;
import store.lunangangster.bpmn.model.Activity;
import store.lunangangster.bpmn.model.BaseElement;
import store.lunangangster.bpmn.model.BpmnModel;
import store.lunangangster.bpmn.model.FlowElement;
import store.lunangangster.bpmn.model.FlowElementsContainer;
import store.lunangangster.bpmn.model.FlowNode;
import store.lunangangster.bpmn.model.Gateway;
import store.lunangangster.bpmn.model.Process;
import store.lunangangster.bpmn.model.SubProcess;

/**
 * BaseElement的xml解析转换器，读取xml文件输入流，将其转换为BaseElement对象
 *
 * @author cwk
 * @version 1.0
 */
public abstract class BaseElementConverter extends AbstractBpmnElementParser {


  public BaseElementConverter() {
    addChildElementParser(new DocumentationParser());
  }


  /**
   * 解析出BaseElement模型对象
   * <p>需要注意的是：当该方法执行完成之后，xsr对象将会读取完整个BaseElement对象标签，并且即将
   * 读取下一个标签。所以不要尝试在该方法调用完成之后再读取当前返回的对象所对应的标签的任何属性信息
   *
   * @param xsr     xml输入流
   * @param model   当前BpmnModel对象
   * @return baseElement对象
   */
  protected abstract BaseElement convertToElement(XMLStreamReader xsr, BpmnModel model) throws Exception;

  /**
   * 将解析出的元素加入到BpmnModel中，更准确地来说是加入到BpmnModel中的Process中
   *
   * <p>该方法最终会调用子类的{@link #convertToElement(XMLStreamReader, BpmnModel)}方法，获取解析到的BaseElement对象。
   * 在调用之前，会做一些前置和后置操作。对外界可以屏蔽调BaseBpmnXmlConverter子类的实现细节，只要是该对象的子类，
   * 就可以通过该方法将解析出的模型的对象添加到BpmnModel中去
   *
   * @param xsr     xml输入流
   * @param model   BpmnModel对象
   */
  protected void convertAndAddToBpmnModel(XMLStreamReader xsr, BpmnModel model, List<SubProcess> nestSubProcesses) throws Exception {
    // get current Process
    Process activityProcess = model.getProcesses().get(model.getProcesses().size() - 1);

    // 提取可能存在的某些属性
    // 由于属性解析必须在convertToElement方法之前，所以需要提前解析
    String id = BaseElementConverter.parseId(xsr);
    String name = FlowElementConverter.parseName(xsr);

    boolean isForCompensation = ActivityConverter.parseCompensation(xsr);
    String defaultSequenceFlow = ActivityConverter.parseDefaultSequenceFlow(xsr);

    // 子类实现
    BaseElement baseElement = convertToElement(xsr, model);
    baseElement.setId(id);

    if (baseElement instanceof FlowElement) {
      FlowElement flowElement = (FlowElement) baseElement;
      flowElement.setName(name);

      if (flowElement instanceof FlowNode) {
        FlowNode flowNode = (FlowNode) flowElement;

        if (flowNode instanceof Activity) {
          Activity activity = (Activity) flowNode;
          activity.setCompensation(isForCompensation);
          activity.setDefaultSequenceFlow(defaultSequenceFlow);
        }

        if (flowNode instanceof Gateway) {
          Gateway gateway = (Gateway) flowNode;
          gateway.setDefaultSequenceFlow(defaultSequenceFlow);
        }

      }

      // 最终解析出的FlowElement都要加入到ParentContainer中去
      FlowElementsContainer parentContainer;
      parentContainer = nestSubProcesses != null && !nestSubProcesses.isEmpty() ?
          nestSubProcesses.get(nestSubProcesses.size() - 1) :
          activityProcess;

      parentContainer.addFlowElement(flowElement);
      flowElement.setParentContainer(parentContainer);
    }
  }


  protected static String parseId(XMLStreamReader xsr) {
    return xsr.getAttributeValue(null, ATTRIBUTE_COMMON_ID);
  }
}
